package com.compass.service;

import com.alibaba.fastjson.JSON;
import com.compass.entity.DubboDo;
import com.compass.mapper.DubboProtocolMapper;
import com.compass.util.DubboParamFormat;
import com.compass.util.TelnetSocket;
import com.compass.vo.Dubbo;
import com.compass.vo.ResponseMessage;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.exception.ZkTimeoutException;
import org.apache.zookeeper.KeeperException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * User: li
 * Date: 2019/3/17
 * Time: 17:23
 * PROJECT：compass
 * PACKAGE: com.compass
 *
 * @author:55324936@qq.com To change this template use File | Settings | File Templates.
 */
@Service
@Slf4j
public class DubboService {

    static Pattern ipPattern =Pattern.compile("(?<=//).*(?=/)");
    static Pattern methodPattern=Pattern.compile("(?<=\\().*(?=\\))");
    static Map paramMap=new HashMap();

    @Autowired
    DubboProtocolMapper dubboProtocolMapper;

    /**
     * 通过接口名解析出服务所在的地址和端口
     * @param zkAddress
     * @param interfaceName
     * **/
    public ResponseMessage resolveIp(String zkAddress,String interfaceName){
        ZkClient zkClient=null;
        try {
            zkClient = new ZkClient(zkAddress,3000);
        }catch (ZkTimeoutException c){
            return ResponseMessage.errorResponse("注册中心连接异常,请确认地址是否正确");
        }
        List<String> dubboNodeList =null;
        try {
             dubboNodeList = zkClient.getChildren("/dubbo/" + interfaceName.trim() + "/providers");
        }catch (ZkNoNodeException z){
            return ResponseMessage.errorResponse("请确认接口是否存在,接口:"+interfaceName);
        }catch (Exception e){
            e.printStackTrace();
        }
        List<String> ipList =new ArrayList<String>();
        for(String s:dubboNodeList){
            try {
                String url =  URLDecoder.decode(s,"UTF-8");
                Matcher matcher =ipPattern.matcher(url);
                String ip=null;
                if(matcher.find()){
                   ip=matcher.group();
                }
                //因为阿里云服务器对外提供跳板机ip所以真是的ip无法访问，如果放到公司内部把这个替换删掉
                ip=ip.replace("172.31.145.55","47.105.55.243");
                ipList.add(ip);
            } catch (UnsupportedEncodingException e) {
                log.error("解析url异常,接口名:{}",interfaceName,e);
            }
        }
        return ResponseMessage.successResponse(ipList);
    }
    /**
     * 通过接口名和ip以及方法名获取已经解析过的参数
     * @param dubbo
     * **/
    public ResponseMessage methodParam(Dubbo dubbo){
        Object paramList = paramMap.get(dubbo.getIp()+dubbo.getInterfaceName()+dubbo.getInterfaceMethod());
        return ResponseMessage.successResponse(JSON.toJSONString(paramList));
    }
    /**
     * 通过接口名和ip解析出来方法
     * @param ip
     * @param interfaceName
     * **/
    public ResponseMessage resolveMethod(String interfaceName,String ip){
        //第一个参数ip 第二个参数端口
        String[] ips = ip.split(":");
        List<String> methodList=new ArrayList<String>();
        Map map=new HashMap();
        try {
            TelnetSocket telnetSocket=new TelnetSocket(ips[0],Integer.parseInt(ips[1]));
            String method =telnetSocket.send("ls -l "+interfaceName);
            method=method.replace("dubbo>","");
            String[] methods =method.split("\r\n");
            for(int i=0;i<methods.length;i++){
                String methodName =methods[i];
                methodName=methodName.substring(methodName.indexOf(" "));
                methodList.add(methodName);

                Matcher matcher= methodPattern.matcher(methodName);
                String methodParam =null;
                while (matcher.find()){
                    methodParam= matcher.group();
                }
                if(StringUtils.isEmpty(methodParam)){
                   continue;
                }
                List<String> paramList = new ArrayList<String>();
                if(methodParam.contains(",")){
                    paramList =Splitter.on(",").trimResults().splitToList(methodParam);
                }else {
                    paramList.add(methodParam);
                }
                paramList= DubboParamFormat.resolveParam(paramList);
                if(0==i) {
                    map.put("param", JSON.toJSONString(paramList));
                }
                //通过Ip端口、接口名、方法名确认入参的唯一性
                paramMap.put(ip+interfaceName+methodName,paramList);
            }
            map.put("methodList",methodList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResponseMessage.successResponse(map);
    }
    /**
     * 调用dubbo接口
     * @param dubbo
     * **/
    public String invoke(Dubbo dubbo){
        String[] ips = dubbo.getIp().split(":");
        try {
            TelnetSocket telnetSocket=new TelnetSocket(ips[0],Integer.parseInt(ips[1]),dubbo.getTimeout());
            telnetSocket.setWriteEncoding(dubbo.getEncoding());
            telnetSocket.setReadEncoding(dubbo.getEncoding());
            String interfaceMethod =dubbo.getInterfaceMethod();
            String methodName =interfaceMethod.substring(0,interfaceMethod.indexOf("(")).trim();
            String cmd;
            if(interfaceMethod.trim().endsWith("()")){
                cmd ="invoke "+dubbo.getInterfaceName()+"."+methodName+"()";
            }else {
                String param =dubbo.getParam();
                param=param.substring(1,param.length()-1);
                //参数1，参数2,
                //[参数1，参数2] 要处理把 开头的[和结尾的]去掉
                cmd ="invoke "+dubbo.getInterfaceName()+"."+methodName+"("+param+")";
            }
            String result=telnetSocket.send(cmd);
            result =result.substring(0,result.indexOf("\r\n"));
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 测试用例调用一次dubbo
     * @param param
     * @param interfaceId
     * **/
    public String invokeForTestCase(Integer interfaceId,String param){
        DubboDo dubboDo =(DubboDo)dubboProtocolMapper.getComponentById(interfaceId);
        dubboDo.setParam(param);
        Dubbo dubbo=new Dubbo();
        BeanUtils.copyProperties(dubboDo,dubbo);
        return this.invoke(dubbo);
    }
}
